#pragma once
#include"util.hpp"
#include"db.hpp"
#include"online.hpp"
#include<vector>
#include<mutex>
#include<unordered_map>
#include<memory>

#define BROAD_ROW 15
#define BROAD_CAL 15

#define CHESS_WHITE 1
#define CHESS_BLACK 2
enum room_status
{
    GEAM_START,
    GEAM_END
};
class room
{
    private:
    //房间的id
    uint64_t _room_id;
    //房间的状态
    room_status _room_statu;
    //房间的总人数
    int _count;
    //白棋玩家的id
    uint64_t _white_id;
    //黑棋玩家的id
    uint64_t _black_id;
    //数据库的操作句柄
    user_table* _db_oparator;
    //用户管理的操作句柄
    online_manager* _user_operator;
    //房间的棋盘
    std::vector<std::vector<int>> _board;

    bool five(int curr_row,int curr_col,int row_off,int col_off,int colour)
    {
        int count=1;
        int row=curr_row+row_off;
        int col=curr_col+col_off;
        while(row>=0&&col>=0&&_board[row][col]==colour&&row<BROAD_ROW&&col<BROAD_CAL)
        {
            count++;
            row+=row_off;
            col+=col_off;
        }
        row=curr_row-row_off;
        col=curr_col-col_off;
        while(row>=0&&col>=0&&_board[row][col]==colour&&row<BROAD_ROW&&col<BROAD_CAL)
        {
            count++;
            row-=row_off;
            col-=col_off;
        }
        if(count>=5)
        {
            return true;
        }
        return false;
    }
    //检查是否满足胜利条件，满足返回获胜方id
    uint64_t checkwin(int row,int col,int colour)
    {
        //横
        bool ret1=five(row,col,0,1,colour);
        //列
        bool ret2=five(row,col,1,0,colour);
        //反斜
        bool ret3=five(row,col,1,1,colour);
        //正斜
        bool ret4=five(row,col,-1,1,colour);
        if(ret1||ret2||ret3||ret4)
        {
            return (colour==CHESS_WHITE?_white_id:_black_id);
        }
        return 0;
    }

    public:
    room(uint64_t room_id,user_table* db_oparator, online_manager* user_operator)
    :_room_id(room_id)
    ,_room_statu(GEAM_START)
    ,_count(0)
    ,_db_oparator(db_oparator)
    ,_user_operator(user_operator)
    ,_board(BROAD_ROW,std::vector<int>(BROAD_CAL,0))
    {
        DBG_LOG("%lu room create success!!",_room_id);
    }
    ~room()
    {
        DBG_LOG("%lu room destory success!!",_room_id);
    }
    //获取房间的id
    uint64_t GetId(){ return _room_id;}
    //获取房间的状态
    room_status GetStatu(){return _room_statu;}
    //获取房间的人数
    int GetCount(){return _count;}
    //加入白棋玩家
    void Add_White_id(uint64_t id){_white_id=id; _count++;}
    //加入黑棋玩家
    void Add_Black_id(uint64_t id){_black_id=id;_count++;}
    //获取白棋玩家id
    uint64_t Get_White_id(){return _white_id;}
    //获取黑棋玩家id
    uint64_t Get_Black_id(){return _black_id;}
    //处理下棋动作
    Json::Value handle_chess(Json::Value& mess)
    {
        Json::Value resp=mess;
        //1.首先判断房间id是否匹配(略)
        //2.分别判断两用户是否在线，如果掉线处理获胜情况
        if(_user_operator->is_in_game_room(_white_id)==false)
        {
            resp["result"]=true;
            resp["reason"]="白方掉线，你获胜了！";
            resp["winner"]=_black_id;
            return resp;
        }
        if(_user_operator->is_in_game_room(_black_id)==false)
        {
            resp["result"]=true;
            resp["reason"]="黑方掉线，你获胜了！";
            resp["winner"]=_white_id;
            return resp;
        }
        //3.判断走棋位置是否合法
        int chess_row=mess["row"].asInt();
        int chess_col=mess["col"].asInt();
        uint64_t curr_uid=mess["uid"].asInt64();
        int color=curr_uid==_white_id?CHESS_WHITE:CHESS_BLACK;
        if(chess_row>=BROAD_ROW||chess_col>=BROAD_CAL||_board[chess_row][chess_col]!=0)
        {
            resp["result"]=false;
            resp["reason"]="落棋位置非法！！";
            return resp;
        }
        _board[chess_row][chess_col]=color;
        //4.判断是否符合胜利条件
        uint64_t winner=checkwin(chess_row,chess_col,color);
        if(winner!=0)
        {
            //说明有获胜方
            resp["reason"]="五星连珠，战无敌！！！";
        }
        resp["result"]=true;
        resp["winner"]=winner;
        return resp;
    }
    //处理聊天动作
    Json::Value handle_chat(Json::Value& mess)
    {
        Json::Value resp=mess;
        //1.首先判断房间id是否匹配（略）
        //2.判断是否有敏感字（略）
        //3.返回消息
        resp["result"]=true;
        return resp;
    }
    //处理玩家退出房间
    void handle_exit(uint64_t userid)
    {
        //如果有玩家在游戏过程中退出则对方胜利
        //如果游戏结束退出则正常退出
        if(_room_statu==GEAM_START)
        {
            Json::Value resp;
            resp["optype"]="put_chess";
            resp["room_id"]=_room_id;
            resp["uid"]=userid;
            resp["result"]=true;
            resp["reason"]="对方掉线，你获胜了！";
            resp["winner"]=(userid==_white_id?_black_id:_white_id);
            broadcast(resp);
        }
        _count--;
        return;
    }
    //处理总的请求
    void handle_request(Json::Value& req)
    {
        Json::Value resp=req;
         //1.首先判断房间id是否匹配
        if(req["room_id"]!=_room_id)
        {
            resp["result"]=false;
            resp["reason"]="房间号不匹配";
            return ;
        }
        //根据不同请求类型调用不同的函数接口
        if(req["optype"]=="put_chess")
        {
            resp=handle_chess(req);
            if(resp["winner"].asInt64()!=0)
            {
                //说明有获胜者
                uint64_t winner=resp["winner"].asInt64();
                uint64_t loser=winner==_white_id?_black_id:_white_id;
                _db_oparator->win(winner);
                _db_oparator->lose(loser);
                _room_statu=GEAM_END;
            }
        }
        else if(req["optype"]=="chat")
        {
            resp= handle_chat(req);
        }
        else
        {
            resp["optype"]=req["optype"].asString();
            resp["result"]=false;
            resp["reason"]="未知类型请求";
        }
        broadcast(resp);
    }
    //将指定信息对房间所有玩家广播
    void broadcast(Json::Value& mess)
    {
        std::string body;
        body=json_util::Serialize(mess,body);
        wsserver_t::connection_ptr wconn=_user_operator->get_conn_from_room(_white_id);
        if(wconn.get()!=nullptr)
        {
            wconn->send(body);
        }
        wsserver_t::connection_ptr bconn=_user_operator->get_conn_from_room(_black_id);
        if(wconn.get()!=nullptr)
        {
            bconn->send(body);
        }
    }
};

typedef std::shared_ptr<room> room_ptr;
class room_mamager
{
    public:
    room_mamager( user_table* db_oparator,online_manager* user_operator)
    :_next_id(1)
    ,_db_oparator(db_oparator)
    ,_user_operator(user_operator)
    {
    }
    ~room_mamager()
    {}

    //两个用户匹配成功后创建一个房间
    room_ptr creat_room(uint64_t uid1,uint64_t uid2)
    {
        //首先判断两个用户是否在线
        if(_user_operator->is_in_game_hall(uid1)==false)
        {
            DBG_LOG("用户掉线，创建房间失败！！");
            return room_ptr();
        }
        if(_user_operator->is_in_game_hall(uid2)==false)
        {
            DBG_LOG("用户掉线，创建房间失败！！");
            return room_ptr();
        }
        std::unique_lock<std::mutex> lock(_mutex);
        //如果都在线那就创建一个房间
        room_ptr rp(new room(_next_id,_db_oparator,_user_operator));
        rp->Add_White_id(uid1);
        rp->Add_Black_id(uid2);
        //建立映射管理关系
        _rooms.insert(std::make_pair(_next_id,rp));
        _uid_to_rid.insert(std::make_pair(uid1,_next_id));
        _uid_to_rid.insert(std::make_pair(uid2,_next_id));
        //计数器++
        _next_id++;
        //返回房间信息：
        DBG_LOG("创建房间成功！！");
        return rp;
    }
    //利用房间id查询对应的房间消息
    room_ptr check_by_rid(uint64_t rid)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        auto pos=_rooms.find(rid);
        if(pos==_rooms.end())
        {
            return room_ptr();
        }
        return pos->second;
    }
    //利用用户id查询所在的房间信息
    room_ptr check_by_uid(uint64_t uid)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        auto pos1=_uid_to_rid.find(uid);
        if(pos1==_uid_to_rid.end())
        {
            return room_ptr();
        }
        auto pos2=_rooms.find(pos1->second);
        if(pos2==_rooms.end())
        {
            return room_ptr();
        }
        return pos2->second;
    }
    //根据房间id删除指定的房间
    void rmove_room(uint64_t rid)
    {
        //首先获取房间信息
        room_ptr rp=check_by_rid(rid);
        if(rp.get()==nullptr)
        {
            return;
        }
      
        //将用户号与房间号的映射关系删除
        uint64_t blackid=rp->Get_Black_id();
        uint64_t whiteid=rp->Get_White_id();
        std::unique_lock<std::mutex> lock(_mutex);
        _uid_to_rid.erase(blackid);
        _uid_to_rid.erase(whiteid);
          //将房间号与房间信息的映射关系删除
        _rooms.erase(rid);
    }
    //删除对应房间中的一个玩家信息，如果玩家已经为0则直接销毁房间
    void rmove_user(uint64_t uid)
    {
        //判断玩家是否在线
        if(_user_operator->is_in_game_room(uid)==false)
        {
            DBG_LOG("玩家未在房间中！！");
            return;
        }
        //首先获取房间id
        auto pos=_uid_to_rid.find(uid);
        if(pos==_uid_to_rid.end())
        {
            DBG_LOG("玩家未在房间中！！");
        }
        //获取房间信息：
        room_ptr rp=check_by_rid(pos->second);
        rp->handle_exit(uid);
        if(rp->GetCount()==0)
        {
            rmove_room(pos->second);
        }
        return;
    }
    private:
    //房间id计数器，用来给每个房间编号
    uint64_t _next_id;
    //房间id与房间消息的映射
    std::unordered_map<uint64_t,room_ptr> _rooms;
    //用户id与房间id的映射
    std::unordered_map<uint64_t,uint64_t> _uid_to_rid;
    //数据库的操作句柄
    user_table* _db_oparator;
    //用户管理的操作句柄
    online_manager* _user_operator;
    //互斥锁
    std::mutex _mutex;
};